/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package moteur.partie;

import Interface.ListenerPartie;
import Interface.ListenerPartie.EventPartie;
import java.util.logging.Level;
import java.util.logging.Logger;
import moteur.*;
import moteur.paquet.*;
import java.util.LinkedList;
import java.util.Vector;
import moteur.carte.Chemin;
import moteur.carte.editor.Graphe;

/**
 *
 * @author Administrateur
 */
public class Partie extends Thread{

    private static final Partie instance = new Partie();
    private Vector<Jouer> joeurs;
    private Vector<ListenerPartie> listeners = new Vector<ListenerPartie>();
    private PionsEnMain tmpPions;


    private boolean continu;
    private long tps1, tps0;
    private LinkedList<Jouer.EventJeu> EventQueue;
    private Graphe g;

    /**
     * Dans l'idée c'est une structure. Classe interne qui définie l'état de la partie.
     */
    public static class Etat {

        /**
         *
         */
        public enum Phase {

            pioche,
            choisirPion,
            placerPion,
            deplacer,
            resolution;
        }
        /**
         * Etat de la partie.
         */
        public static Phase etatActuel = null;
        /**
         * n° du tour
         */
        public static int tour = 0;
        /**
         * index du joeur courrant
         */
        public static int joeurCourant = 0;
        /**
         * tps écoulé depuis la derniere action perçu du joeur.
         */
        public static int tpsPassé = 0;

        private static int cptPhase = 0;


        /**
         * pour le debuggage, affiche l'état de la partie sur la sortie standard.
         */
        public static void afficherEtat(){
            System.out.println(
                    "Phase: "+ etatActuel.name()
                    +" tour: " + tour
                    + " joeurCourant: " + joeurCourant
                    + " cptPhase: " + cptPhase
                    + " tpsPassé: " + tpsPassé
            );
        }


        /**
         * remet tous les paramêtre à 0 ou null.
         */
        public static void reset() {
            etatActuel = null;
            tour = 0;
            joeurCourant = 0;
            tpsPassé = 0;
            cptPhase = 0;
        }

        /**
         * Fait avancer la partie.
         */
        public static void next() {
            
            Partie p = Partie.getInstance();
            switch (etatActuel) {

                case pioche:

                    if ((cptPhase < p.joeurs.size()) && !p.tlmAPassé()) {
                        joeurCourant = cptPhase;
                        cptPhase++;
                        p.defausse();

                    } else {
                        for (Jouer j : p.getJoeurs()){
                            j.pioche();
                        }
                        cptPhase = 0;
                        p.resetAPassé();
                        etatActuel = Phase.choisirPion;
                        joeurCourant = tour % p.joeurs.size();
                        next();
                    }
                    break;
                case choisirPion:
                    if ((cptPhase < p.joeurs.size() * Constante.maxPionChoisi) && !p.tlmAPassé()) {
                        joeurCourant = (tour + cptPhase) % p.joeurs.size();
                        cptPhase++;
                        p.choisirPion();

                    } else {
                        cptPhase = 0;
                        etatActuel = Etat.Phase.placerPion;
                        p.resetAPassé();
                        next();
                    }
                    break;
                case placerPion:
                    if (!p.tlmAPassé()) {
                        joeurCourant = (tour + cptPhase++) % p.joeurs.size();
                        p.placerPion();
                    } else {
                        cptPhase = 0;
                        etatActuel = Etat.Phase.deplacer;
                        p.resetAPassé();
                        next();
                    }
                    break;
                case deplacer:
                    if (!p.tlmAPassé()) {
                        joeurCourant = (tour + cptPhase++) % p.joeurs.size();
                        p.deplacer();
                    } else {
                        cptPhase = 0;
                        etatActuel = Phase.resolution;
                        p.resetAPassé();
                        next();
                    }
                    break;
                case resolution:
                    etatActuel = Etat.Phase.pioche;
                    p.resolution();
                    tour++;
                    
                    break;
                default:
                    if (!p.joeurs.isEmpty()) { 
                        p.joeurs.trimToSize(); //pour qu'il n'y ai pas de "bulle" dans le vecteur.
                        etatActuel = Phase.pioche;
                        //next();
                    }
                    break;
            }
            //afficherEtat();
        }
    }
public PionsEnMain getTmpPions() {
        return tmpPions;
    }
    /**
     * Verifie si le joeur a joué (incrémente la phase).
     * 
     * Execute la fonction associé a la phase en cours.
     */
    private void go() {

        Etat.tpsPassé += Constante.tpsBoucle;
        if (Etat.tpsPassé > Constante.maxTpsJeu) {
            joeurs.get(Etat.joeurCourant).passe(); //passe doit envoyer un message inutile
            Etat.tpsPassé = 0;
            Etat.next();
        }
//*
        for(Jouer.EventJeu e = EventQueue.poll(); e != null; e = EventQueue.poll()){
            if (e.fromJoeur.equals(joeurs.get(Etat.joeurCourant))) {
                Etat.tpsPassé = 0;
                Etat.next();
                //EventQueue.clear();
            }
        }
//*/
        /*
        Jouer.EventJeu e = EventQueue.pollFirst();

        while (e != null) {
            if (e.fromJoeur == joeurs.get(Etat.joeurCourant)) {
                Etat.tpsPassé = 0;
                Etat.next();
               // EventQueue.clear();
            } else {
                e = EventQueue.pollFirst();
            }
        }
 //*/
    }

    private Partie() {
        super();
        joeurs = new Vector<Jouer>(Constante.nbJoeur, 1);
        EventQueue = new LinkedList<Jouer.EventJeu>();
        tmpPions = new PionsEnMain();
        continu = true;
    }

    /**
     * Getteur de partie, car singleton.
     * @return
     */
    public static Partie getInstance() {
        return instance;
    }

    /**
     *
     * @param e
     */
    public void addJoeur(Jouer e) {
        joeurs.add(e);
    }

    /**
     *
     * @return
     */
    public Vector<Jouer> getJoeurs() {
        return joeurs;
    }

    /**
     * Démarre la partie.
     */
    @Override
    public void run() {

        init();
        tps1 = System.currentTimeMillis();
        tps0 = tps1 - Constante.tpsBoucle; //pour démarrer directement (inutilo-indispensable)

        while (continu) {

            tps1 = System.currentTimeMillis();
            if (tps1 - tps0 > Constante.tpsBoucle) {

                go(); // gere event et phase de jeu.

                tps0 = tps1;
            } else {
                try {
                    Thread.sleep(Constante.tpsBoucle - tps1 + tps0); //tps0+tpsBoucle = heure de la prochaine itération, - tp1 heure du début de l'itération
                } catch (InterruptedException ex) {
                    Logger.getLogger(Partie.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }

    }


    private void init() {
        Etat.etatActuel = Etat.Phase.pioche;
        Etat.next();
    }

    private void choisirPion() {

        Jouer j = joeurs.get(Etat.joeurCourant);
        if (!j.aPasse()) {
            firePartieEvent(Etat.etatActuel, j, Etat.tour);
            try {
                tmpPions.piocher(5 - tmpPions.total(), PaquetPionTransport.getInstance());
            } catch (Exception e) {
                System.err.print(tmpPions.toString() + "" + tmpPions.toStringCaché()+" " +PaquetPionTransport.getInstance().toString());
            }
            j.choisiPion(tmpPions);
        } else {
            Etat.next();
        }
    }

    private void deplacer() {
        Jouer j = joeurs.get(Etat.joeurCourant); 
        if (!j.aPasse()) {
            firePartieEvent(Etat.etatActuel, j, Etat.tour);
            j.deplacer();
        } else {
            Etat.next();
        }
    }

    private void placerPion() {
        Jouer j = joeurs.get(Etat.joeurCourant);
      
        if (!j.aPasse()) {
            firePartieEvent(Etat.etatActuel, j, Etat.tour);
            j.placePion();
        } else {
            Etat.next();
        }
    }

    private void defausse(){
        Jouer j = joeurs.get(Etat.joeurCourant);
        firePartieEvent(Etat.etatActuel, j , Etat.tour);
        
        if (!j.aPasse()) {
            j.defausse();
            
        }else{
            Etat.next();
        }

    }

    private void resolution() {
        firePartieEvent(Etat.etatActuel, null, Etat.tour);
              Vector<Chemin> vc = g.getListeChemin();
        for(Chemin c : vc){
        c.resetPion();
        }
        if(Etat.tour >= 3){
            for(Jouer j: joeurs){
            System.out.println(j.getNom() + " : " + j.getScore());
            }
            this.finir();
        }else{
            Etat.next();
        }


    }

    private void resetAPassé() {

        for (Jouer j : joeurs) {
            j.naPasPasse();
        }

    }

    private boolean tlmAPassé() {
        boolean b = true;

        for (Jouer j : joeurs) {
            b = b && j.aPasse();
        }

        return b;
    }

    /**
     * Permet aux joeurs d'informer qu'ils ont joués.
     * @param e
     */
    public void receiveEvent(Jouer.EventJeu e) {
        EventQueue.add(e);
    }

    /**
     *
     * @return
     */
    public boolean enPhasePioche(){
        return Etat.etatActuel == Etat.Phase.pioche;
    }

    /**
     * Arrte la partie en cours. Définitivement. C'est pas qu'une pause.
     */
    public void finir() {
        continu = false;
        Etat.reset();
    }

    /**
     *
     * @param p
     */
    public void addListener(ListenerPartie p){
        listeners.add(p);
    }
    /**
     *
     * @param p
     */
    public void removeListener(ListenerPartie p){
        listeners.remove(p);
    }

    private void firePartieEvent(Etat.Phase etat, Jouer j, int tour){
    EventPartie e = new EventPartie(etat, j, tour);
        for (ListenerPartie l: listeners){
        l.update(e);
        }
    }

    /**
     *
     * @return
     */
    public Graphe getG() {
        return g;
    }

    /**
     *
     * @param g
     */
    public void setG(Graphe g) {
        this.g = g;
    }

    /**
     *
     * @param j
     * @return l'index du joeur j.
     */
    public int getIndex(Jouer j){
    return joeurs.indexOf(j);
    }

    public Jouer getJoeurCourant(){
        return joeurs.elementAt(Etat.joeurCourant);
    }

}
